﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows.Data;
using System.Windows;
using System.Runtime.Serialization;
using System.IO;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Diagnostics.CodeAnalysis;
using System.ComponentModel.DataAnnotations;

namespace DarkLight.Model
{
    [DataContract]
    public class SolarSystem : Orthonormality
    {
    //    #region Data
    //    private Effect _effect;
    //    private InputLayout _layout;

    //    private List<Vector3> _vertices;
    //    private List<int> _indices;
    //    #endregion

        #region Constructeurs
        public SolarSystem()
        {
            Planets = new ObservableCollection<Planet>();
        }

        //~SolarSystem()
        //{
        //    Dispose(false);
        //}
        #endregion

        #region Membres publics
        [DataMember]
        [Required(ErrorMessage = "Name required")]
        public string Name
        {
            get { return _name; }
            set
            {
                if (_name != value)
                {
                    RaisePropertyChanging("Name");
                    _name = value;
                    RaisePropertyChanged("Name");
                }
            }
        }
        private string _name;

        [DataMember]
        public Sun Sun
        {
            get { return _Sun; }
            set
            {
                if (_Sun != value)
                {
                    RaisePropertyChanging("Sun");
                    _Sun = value;
                    RaisePropertyChanged("Sun");
                }
            }
        }
        private Sun _Sun;

        [DataMember]
        public ObservableCollection<Planet> Planets { get; private set; }
        
        public IPosition SelectedItem
        { 
            get { return _SelectedItem; }
            set
            { 
                if (_SelectedItem != value)
                {
                    RaisePropertyChanging("SelectedItem");
                    _SelectedItem = value;
                    RaisePropertyChanged("SelectedItem");
                }
            }
        }
        private IPosition _SelectedItem;
        
        public void RemoveSkybox()
        {
        //    _Skybox = null;
        //    this._effect.GetVariableByName("g_EnvironmentTexture").AsResource().SetResource(null);
        }

        public void LoadSkyboxFromFile(string filename)
        {
            //ShaderResourceView sRV = null;

            //try
            //{
            //    var tex = TextureHelper.LoadTextureFromFile(filename);
            //    _Skybox = File.ReadAllBytes(filename);
            //    sRV = object.ReferenceEquals(tex, null) ? null : new ShaderResourceView(Engine.Current.Device, tex);
            //    this._effect.GetVariableByName("g_EnvironmentTexture").AsResource().SetResource(sRV);
            //}
            //finally
            //{
            //    sRV.Dispose();
            //    sRV = null;
            //}
        }
        #endregion

        #region ObservableObject Overrides
        protected override void OnPropertyChanging(System.ComponentModel.PropertyChangingEventArgs e)
        {
            base.OnPropertyChanging(e);
            if (!object.ReferenceEquals(Planets, null))
            {
                foreach (var planet in Planets)
                    planet.ParentOrthonormality = null;
                Planets.CollectionChanged -= Planets_CollectionChanged;
            }
        }

        protected override void OnPropertyChanged(System.ComponentModel.PropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);
            if (!object.ReferenceEquals(Planets, null))
            {
                foreach (var planet in Planets)
                    planet.ParentOrthonormality = this;
                Planets.CollectionChanged += Planets_CollectionChanged;
            }
        }

        private void Planets_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
                foreach (Planet planet in e.NewItems)
                    planet.ParentOrthonormality = this;
            else if (e.Action == NotifyCollectionChangedAction.Remove)
                foreach (Planet planet in e.OldItems)
                    planet.ParentOrthonormality = null;
        }
        #endregion

        #region IOrthonormality Implementation
        protected internal override IEnumerable<IPosition> ChildElements
        {
            get
            {
                var childs = new List<IPosition>();
                childs.AddRange(this.Planets);
                childs.AddRange(ConvoyManager.GetConvoys());
                return childs;
            }
        }
        #endregion

        #region Serialization Members
        [DataMember]
        [SuppressMessage("Microsoft.Performance", "CA1811")]
        byte[] Skybox
        {
            get
            {
                return _Skybox;
            }
            set
            {
                if (_Skybox != value)
                {
                    _Skybox = value;
                    var file = "skybox.dds";
                    File.WriteAllBytes(file, _Skybox);
                    LoadSkyboxFromFile(file);
                    File.Delete(file);
                }
            }
        }
        byte[] _Skybox;
        #endregion

        //#region Membres privés
        //private void Initialize()
        //{
        //    CreateEffect();
        //    CreateGeometry();
        //}

        //private void CreateEffect()
        //{
        //    using (var ms = new MemoryStream())
        //    {
        //        ms.Write(Properties.Resources.System, 0, Properties.Resources.System.GetLength(0));
        //        ms.Position = 0;
        //        ms.Flush();
        //        this._effect = Effect.FromStream(Engine.Current.Device, ms, "fx_4_0", ShaderFlags.None, EffectFlags.None);
        //    }

        //    //Crée le layout de rendu
        //    var inputElements = new[] 
        //    {
        //        new InputElement("POSITION", 0, Format.R32G32B32_Float, 0, 0)
        //    };

        //    EffectTechnique technique = this._effect.GetTechniqueByName("Skybox");
        //    EffectPass firstPass = technique.GetPassByIndex(0);
        //    _layout = new InputLayout(Engine.Current.Device, firstPass.Description.Signature, inputElements);
        //}

        //private void CreateGeometry()
        //{
        //    this._vertices = new List<Vector3>
        //    {
        //        new Vector3(-1.0f, 1.0f, -1.0f),
        //        new Vector3(1.0f, 1.0f, -1.0f),
        //        new Vector3(1.0f, 1.0f, 1.0f), 
        //        new Vector3(-1.0f, 1.0f, 1.0f), 

        //        new Vector3(-1.0f, -1.0f, -1.0f), 
        //        new Vector3(1.0f, -1.0f, -1.0f), 
        //        new Vector3(1.0f, -1.0f, 1.0f), 
        //        new Vector3(-1.0f, -1.0f, 1.0f), 

        //        new Vector3(-1.0f, -1.0f, 1.0f),
        //        new Vector3(-1.0f, -1.0f, -1.0f),
        //        new Vector3(-1.0f, 1.0f, -1.0f), 
        //        new Vector3(-1.0f, 1.0f, 1.0f), 

        //        new Vector3(1.0f, -1.0f, 1.0f), 
        //        new Vector3(1.0f, -1.0f, -1.0f), 
        //        new Vector3(1.0f, 1.0f, -1.0f), 
        //        new Vector3(1.0f, 1.0f, 1.0f), 

        //        new Vector3(-1.0f, -1.0f, -1.0f), 
        //        new Vector3(1.0f, -1.0f, -1.0f),
        //        new Vector3(1.0f, 1.0f, -1.0f), 
        //        new Vector3(-1.0f, 1.0f, -1.0f),

        //        new Vector3(-1.0f, -1.0f, 1.0f),
        //        new Vector3(1.0f, -1.0f, 1.0f),
        //        new Vector3(1.0f, 1.0f, 1.0f),
        //        new Vector3(-1.0f, 1.0f, 1.0f)
        //    };

        //    this._indices = new List<int>()
        //    {
        //        3,1,0,
        //        2,1,3,

        //        6,4,5,
        //        7,4,6,

        //        11,9,8,
        //        10,9,11,

        //        14,12,13,
        //        15,12,14,

        //        19,17,16,
        //        18,17,19,

        //        22,20,21,
        //        23,20,22
        //    };
        //}
        //#endregion

        //#region Rendering
        //float IHitElement.Radius
        //{
        //    get { return Radius; }
        //}

        //void IRender3D.Render3D()
        //{
        //    Render3D();
        //}

        //protected internal virtual void Render3D()
        //{
        //    Buffer indexesBuffer = null;
        //    Buffer verticesBuffer = null;

        //    try
        //    {
        //        //Calcul des variables du shader
        //        var viewMatrix = SceneHelper.GetViewMatrix();
        //        var projMatrix = SceneHelper.GetProjectionMatrix();
        //        var skyboxMatrix = viewMatrix;
        //        skyboxMatrix.M41 = 0;
        //        skyboxMatrix.M42 = 0;
        //        skyboxMatrix.M43 = 0;

        //        //Affectation des variables du shader
        //        this._effect.GetVariableByName("g_mView").AsMatrix().SetMatrix(skyboxMatrix);
        //        this._effect.GetVariableByName("g_mProjection").AsMatrix().SetMatrix(projMatrix);

        //        //Créer les différents buffers
        //        indexesBuffer = BufferHelper.CreateBuffer<int>(this._indices, true);
        //        verticesBuffer = BufferHelper.CreateBuffer<Vector3>(this._vertices);

        //        //Créée le buffer de vertex
        //        var binding = new[]
        //        {
        //            new VertexBufferBinding(verticesBuffer, Marshal.SizeOf(typeof(Vector3)), 0)
        //        };

        //        //Envoie les données à l'Input Assembler
        //        Engine.Current.Device.InputAssembler.SetInputLayout(this._layout);
        //        Engine.Current.Device.InputAssembler.SetPrimitiveTopology(PrimitiveTopology.TriangleList);
        //        Engine.Current.Device.InputAssembler.SetIndexBuffer(indexesBuffer, Format.R32_UInt, 0);
        //        Engine.Current.Device.InputAssembler.SetVertexBuffers(0, binding);

        //        //Effectue les différentes passes
        //        var technique = this._effect.GetTechniqueByName("Skybox");
        //        for (int pass = 0; pass < technique.Description.PassCount; ++pass)
        //        {
        //            technique.GetPassByIndex(pass).Apply();
        //            Engine.Current.Device.DrawIndexed(_indices.Count, 0, 0);
        //        }
        //    }
        //    finally
        //    {
        //        //Réinitialise les buffers
        //        indexesBuffer.Dispose();
        //        indexesBuffer = null;
        //        verticesBuffer.Dispose();
        //        verticesBuffer = null;
        //        Engine.Current.Device.InputAssembler.SetIndexBuffer(null, Format.Unknown, 0);
        //        Engine.Current.Device.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding());
        //    }
        //}

        //protected float Radius
        //{
        //    get { return 0; }
        //}
        //#endregion

        //#region IDisposable Implementation
        //public void Dispose()
        //{
        //    Dispose(true);
        //    GC.SuppressFinalize(this);
        //}

        //protected virtual void Dispose(bool disposing)
        //{
        //    if (disposing)
        //    {
        //        if (_layout != null)
        //        {
        //            _layout.Dispose();
        //            _layout = null;
        //        }
        //    }
        //}
        //#endregion

        //#region Membres privés
        //private void RenderBloom()
        //{
        //    Vector2[] vSampleOffsets = new Vector2[MAX_SAMPLES];
        //    float[] fSampleOffsets = new float[MAX_SAMPLES];
        //    Vector4[] vSampleWeights = new Vector4[MAX_SAMPLES];

        //    //Surface pSurfScaledHDR;
        //    //pSurfScaledHDR = g_pTexSceneScaled.AsSurface();

        //    //Surface pSurfHDR = g_pTexScene;
        //    //g_pTexScene->GetSurfaceLevel(0, pSurfHDR);

        //    Surface pSurfBloom = pSurfBloom = g_apTexBloom[0].AsSurface();
        //    Surface pSurfTempBloom = g_apTexBloom[1].AsSurface();
        //    Surface pSurfBloomSource = g_apTexBloom[2].AsSurface();

        //    // Clear the bloom texture
        //    //g_pd3dDevice->ColorFill(pSurfBloom, NULL, D3DCOLOR_ARGB(0, 0, 0, 0));

        //    Rect rectSrc = new Rect(0, 0, g_pTexBloomSource.Description.Width, g_pTexBloomSource.Description.Height);
        //    rectSrc.Inflate(-1, -1);

        //    Rect rectDest = new Rect(0, 0, g_apTexBloom[2].Description.Width, g_apTexBloom[2].Description.Height);
        //    rectDest.Inflate(-1, -1);

        //    CoordRect coords = GetTextureCoords(g_pTexBloomSource, rectSrc, g_apTexBloom[2], rectDest);

        //    SurfaceDescription desc = g_pTexBloomSource.AsSurface().Description;

        //    this.GetSampleOffsets_GaussBlur5x5(desc.Width, desc.Height, vSampleOffsets, vSampleWeights, 1.0f);

        //    this.effect.GetVariableByName("g_avSampleOffsets").AsVector().Set(vSampleOffsets.Select(v => new Vector4(v.X, v.Y, 0, 0)).ToArray());
        //    this.effect.GetVariableByName("g_avSampleWeights").AsVector().Set(vSampleWeights);

        //    var technique = effect.GetTechniqueByName("GaussBlur5x5");
        //    for (var pass = 0; pass < technique.Description.PassCount; pass++)
        //    {
        //        technique.GetPassByIndex(pass).Apply();
        //    }
        //}

        //private CoordRect GetTextureCoords(Texture2D pTexSrc, Rect pRectSrc, Texture2D pTexDest, Rect pRectDest)
        //{
        //    CoordRect pCoords = new CoordRect();
        //    SurfaceDescription desc;
        //    float tU, tV;

        //    // Validate arguments
        //    if (pTexSrc == null || pTexDest == null)
        //        throw new ArgumentException();

        //    // Start with a default mapping of the complete source surface to complete 
        //    // destination surface
        //    pCoords.fLeftU = 0.0f;
        //    pCoords.fTopV = 0.0f;
        //    pCoords.fRightU = 1.0f;
        //    pCoords.fBottomV = 1.0f;

        //    // If not using the complete source surface, adjust the coordinates
        //    if (pRectSrc != null)
        //    {
        //        // Get destination texture description
        //        desc = pTexSrc.AsSurface().Description;

        //        // These delta values are the distance between source texel centers in 
        //        // texture address space
        //        tU = 1.0f / desc.Width;
        //        tV = 1.0f / desc.Height;

        //        pCoords.fLeftU += (float)pRectSrc.Left * tU;
        //        pCoords.fTopV += (float)pRectSrc.Top * tV;
        //        pCoords.fRightU -= (float)(desc.Width - pRectSrc.Right) * tU;
        //        pCoords.fBottomV -= (float)(desc.Height - pRectSrc.Bottom) * tV;
        //    }

        //    // If not drawing to the complete destination surface, adjust the coordinates
        //    if (pRectDest != null)
        //    {
        //        // Get source texture description
        //        desc = pTexDest.AsSurface().Description;

        //        // These delta values are the distance between source texel centers in 
        //        // texture address space
        //        tU = 1.0f / desc.Width;
        //        tV = 1.0f / desc.Height;

        //        pCoords.fLeftU -= (float)pRectDest.Left * tU;
        //        pCoords.fTopV -= (float)pRectDest.Top * tV;
        //        pCoords.fRightU += (float)(desc.Width - pRectDest.Right) * tU;
        //        pCoords.fBottomV += (float)(desc.Height - pRectDest.Bottom) * tV;
        //    }

        //    return pCoords;
        //}

        //private void GetSampleOffsets_Bloom(float dwD3DTexSize, float[] afTexCoordOffset, Vector4[] avColorWeight, float fDeviation, float fMultiplier)
        //{
        //    int i = 0;
        //    float tu = 1.0f / (float)dwD3DTexSize;

        //    // Fill the center texel
        //    float weight = fMultiplier * GaussianDistribution(0, 0, fDeviation);
        //    avColorWeight[0] = new Vector4(weight, weight, weight, 1.0f);

        //    afTexCoordOffset[0] = 0.0f;

        //    // Fill the first half
        //    for (i = 1; i < 8; i++)
        //    {
        //        // Get the Gaussian intensity for this offset
        //        weight = fMultiplier * GaussianDistribution((float)i, 0, fDeviation);
        //        afTexCoordOffset[i] = i * tu;

        //        avColorWeight[i] = new Vector4(weight, weight, weight, 1.0f);
        //    }

        //    // Mirror to the second half
        //    for (i = 8; i < 15; i++)
        //    {
        //        avColorWeight[i] = avColorWeight[i - 7];
        //        afTexCoordOffset[i] = -afTexCoordOffset[i - 7];
        //    }
        //}

        ///// <summary>
        ///// Obtient les offsets à utiliser dans le Blur gaussien 5x5 sur les coordonnées de texture
        ///// </summary>
        ///// <param name="texWidth">Largeur de la texture en pixels</param>
        ///// <param name="texHeight">Hauteur de la texture en pixels</param>
        ///// <param name="texCoordOffset">Liste des offsets</param>
        ///// <param name="sampleWeight">liste des poids des offsets</param>
        ///// <param name="multiplier">Amplificateur de blur</param>
        //private void GetSampleOffsets_GaussBlur5x5(float texWidth, float texHeight, Vector2[] texCoordOffset, Vector4[] sampleWeight, float multiplier)
        //{
        //    float tu = 1.0f / texWidth;
        //    float tv = 1.0f / texHeight;

        //    Vector4 white = new Vector4(1.0f, 1.0f, 1.0f, 1.0f); //Couleur blanche

        //    float totalWeight = 0.0f;
        //    int index = 0;
        //    for( int x = -2; x <= 2; x++ )
        //    {
        //        for( int y = -2; y <= 2; y++ )
        //        {
        //            if (Math.Abs(x) + Math.Abs(y) > 2)
        //                continue;

        //            texCoordOffset[index] = new Vector2(x * tu, y * tv );
        //            sampleWeight[index] = white * GaussianDistribution( ( float )x, ( float )y, 1.0f );
        //            totalWeight += sampleWeight[index].X;

        //            index++;
        //        }
        //    }

        //    for( int i = 0; i < index; i++ )
        //    {
        //        sampleWeight[i] /= totalWeight;
        //        sampleWeight[i] *= multiplier;
        //    }
        //}

        ///// <summary>
        ///// Calcule le coefficient de distribution Gaussienne
        ///// </summary>
        ///// <param name="x">Abscisse relative au pixel central</param>
        ///// <param name="y">Ordonnée relative au pixel central</param>
        ///// <param name="rho">Rayon d'exposition</param>
        ///// <returns>Coefficient de distribution Gaussienne</returns>
        //private float GaussianDistribution(float x, float y, float rho)
        //{
        //    float g = 1.0f / (float)Math.Sqrt(2.0f * Math.PI * rho * rho);
        //    g *= (float)Math.Exp(-(x * x + y * y) / (2 * rho * rho));

        //    return g;
        //}
        //#endregion
    }
}
